﻿#region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using Csla;
    using Csla.Data;
    using Csla.Reflection;
    using Csla.Server;

    using MarketPulse.DataAccess.DataContextObjects.Production;

    using Common = MarketPulse.BusinessLogic.Common;
    using MarketPulse.BusinessLogic.Common.Criteria;
    using Private = MarketPulse.BusinessLogic.Private;

#endregion

namespace MarketPulse.DataAccess.Factory
{
    /// <summary>
    /// Abstracts the data access for a User away from a User Business Object.
    /// </summary>
    public class UserFactory : ObjectFactory
    {
        #region Member Variables

            /// <summary>
            /// Property that stores the name of the class.
            /// </summary>
            private static readonly Type mClass = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;

            /// <summary>
            /// Property that stores the log4net logger for this class.
            /// </summary>
            private static readonly log4net.ILog mLog = log4net.LogManager.GetLogger(mClass);

        #endregion

        #region Methods

            #region Create Methods

                /// <summary>
                /// Method used to create and return a new User object instance
                /// </summary>
                /// <returns>The newly created User object</returns>
                [RunLocal]
                public Private.User Create()
                {
                    // Create a new instance of the object
                    var user = (Private.User)MethodCaller.CreateInstance(typeof(Private.User));

                    user.Obsolete = false;
                    user.FailedLogonAttempts = 0;

                    // Mark it as a new object
                    MarkNew(user);

                    //Check the business rules for this object
                    CheckRules(user);

                    return user;
                }

            #endregion

            #region Fetch Methods

                #region User

                    /// <summary>
                    /// Method used to fetch a user object from the database
                    /// </summary>
                    /// <param name="criteria">The GUID of User object to persist</param>
                    /// <returns>The User object, potentially altered by the act of persisting it</returns>
                    public Private.User Fetch(SingleCriteria<Guid> criteria)
                    {
                        try
                        {
                            using (var manager = ContextManager<MUM_dev>.GetManager(Database.BusinessSystem))
                            {
                                // Create an instance to return
                                var user = (Private.User)MethodCaller.CreateInstance(typeof(Private.User));

                                // Fetch the data from the database based on the criteria
                                var selectResult = manager.DataContext.UspUserSelect(criteria.Value);

                                // Get the result
                                UspUserSelectResult result = null;
                                try
                                {
                                    result = selectResult.Single();
                                }
                                catch (InvalidOperationException ioe)
                                {
                                    // Construct error message
                                    StringBuilder errorMessage = new StringBuilder();
                                    errorMessage.Append("Unable to locate exactly one User with Guid = [ ");
                                    errorMessage.Append(criteria.Value);
                                    errorMessage.Append(" ]");
                                    mLog.Error(errorMessage.ToString(), ioe);
                                    throw new DataAccessException(errorMessage.ToString());
                                }

                                // Common properties inherited from MarketPulseBase<Visit>
                                LoadProperty(user, Private.User.GUIDProperty, criteria.Value);
                                LoadProperty(user, Private.User.ObsoleteProperty, result.Obsolete.Value);
                                LoadProperty(user, Private.User.InsertDateTimeProperty, result.InsertDateTime.Value);
                                LoadProperty(user, Private.User.InsertUserGUIDProperty, result.InsertUserGUID.Value);
                                LoadProperty(user, Private.User.UpdateDateTimeProperty, result.UpdateDateTime.Value);
                                LoadProperty(user, Private.User.UpdateUserGUIDProperty, result.UpdateUserGUID.Value);
                                // Other properties
                                LoadProperty(user, Private.User.UsernameProperty, result.UserName);
                                LoadProperty(user, Private.User.FirstnameProperty, result.Firstname);
                                LoadProperty(user, Private.User.LastNameProperty, result.Lastname);
                                LoadProperty(user, Private.User.EMailProperty, result.EMail);

                                // Make sure that CSLA knows this has changed since being instantiated
                                MarkOld(user);

                                return user;
                            }
                        }
                        catch (DataAccessException dae)
                        {
                            mLog.Error(dae.ToString());
                            throw dae;
                        }
                        catch (Exception ex)
                        {
                            mLog.Error(ex.ToString());
                            throw ex;
                        }
                    }

                    /// <summary>
                    /// Method used to fetch a user object from the database
                    /// </summary>
                    /// <param name="criteria">The criteria object to use to retrieve the user</param>
                    /// <returns>A valid (populated) User object</returns>
                    public Private.User Fetch(SingleCriteria<string> criteria)
                    {
                        try
                        {
                            using (var manager = ContextManager<MUM_dev>.GetManager(Database.BusinessSystem))
                            {
                                // Create an instance to return
                                var user = (Private.User)MethodCaller.CreateInstance(typeof(Private.User));

                                // Fetch the data from the database based on the criteria
                                var selectResult = manager.DataContext.UspUserSelectByUsername(criteria.Value);

                                // Get the result
                                UspUserSelectByUsernameResult result = null;
                                try
                                {
                                    result = selectResult.Single();
                                }
                                catch (InvalidOperationException ioe)
                                {
                                    // Construct error message
                                    StringBuilder errorMessage = new StringBuilder();
                                    errorMessage.Append("Unable to locate exactly one User with Username = [ ");
                                    errorMessage.Append(criteria.Value);
                                    errorMessage.Append(" ]");
                                    mLog.Error(errorMessage.ToString(), ioe);
                                    throw new DataAccessException(errorMessage.ToString());
                                }

                                // Common properties inherited from MarketPulseBase<Visit>
                                LoadProperty(user, Private.User.GUIDProperty, result.UserGUID);
                                LoadProperty(user, Private.User.ObsoleteProperty, result.Obsolete.Value);
                                LoadProperty(user, Private.User.InsertDateTimeProperty, result.InsertDateTime.Value);
                                LoadProperty(user, Private.User.InsertUserGUIDProperty, result.InsertUserGUID.Value);
                                LoadProperty(user, Private.User.UpdateDateTimeProperty, result.UpdateDateTime.Value);
                                LoadProperty(user, Private.User.UpdateUserGUIDProperty, result.UpdateUserGUID.Value);
                                // Other properties
                                LoadProperty(user, Private.User.UsernameProperty, criteria.Value);
                                LoadProperty(user, Private.User.FirstnameProperty, result.Firstname);
                                LoadProperty(user, Private.User.LastNameProperty, result.Lastname);
                                LoadProperty(user, Private.User.EMailProperty, result.EMail);

                                // Make sure that CSLA knows this has changed since being instantiated
                                MarkOld(user);

                                return user;
                            }
                        }
                        catch (DataAccessException dae)
                        {
                            mLog.Error(dae.ToString());
                            throw dae;
                        }
                        catch (Exception ex)
                        {
                            mLog.Error(ex.ToString());
                            throw ex;
                        }
                    }

                #endregion

                #region UserInfo

                    /// <summary>
                    /// Method used to fetch an existing (Read Only) User from the database given a guid
                    /// </summary>
                    /// <param name="criteria">The criteria object holding the Guid of the User to retrieve</param>
                    /// <returns>The (read only) User object fetched from the database</returns>
                    public Common.UserInfo FetchInfo(SingleCriteria<Guid> criteria)
                    {
                        try
                        {
                            using (var manager = ContextManager<MUM_dev>.GetManager(Database.BusinessSystem))
                            {
                                // Create an instance to return
                                var userInfo = (Common.UserInfo)MethodCaller.CreateInstance(typeof(Common.UserInfo));

                                // Fetch the data from the database based on the criteria
                                var selectResult = manager.DataContext.UspUserSelect(criteria.Value);

                                // Get the result
                                UspUserSelectResult result = null;
                                try
                                {
                                    result = selectResult.Single();
                                }
                                catch (InvalidOperationException ioe)
                                {
                                    // Construct error message
                                    StringBuilder errorMessage = new StringBuilder();
                                    errorMessage.Append("Unable to locate exactly one User with Guid = [ ");
                                    errorMessage.Append(criteria.Value);
                                    errorMessage.Append(" ]");
                                    mLog.Error(errorMessage.ToString(), ioe);
                                    throw new DataAccessException(errorMessage.ToString());
                                }

                                // Common properties inherited from MarketPulseBase<Visit>
                                LoadProperty(userInfo, Common.UserInfo.GUIDProperty, criteria.Value);
                                LoadProperty(userInfo, Common.UserInfo.ObsoleteProperty, result.Obsolete.Value);
                                LoadProperty(userInfo, Common.UserInfo.InsertDateTimeProperty, result.InsertDateTime.Value);
                                LoadProperty(userInfo, Common.UserInfo.InsertUserGUIDProperty, result.InsertUserGUID.Value);
                                LoadProperty(userInfo, Common.UserInfo.UpdateDateTimeProperty, result.UpdateDateTime.Value);
                                LoadProperty(userInfo, Common.UserInfo.UpdateUserGUIDProperty, result.UpdateUserGUID.Value);
                                // Other properties
                                LoadProperty(userInfo, Common.UserInfo.mUsernameProperty, result.UserName);
                                LoadProperty(userInfo, Common.UserInfo.mFirstnameProperty, result.Firstname);
                                LoadProperty(userInfo, Common.UserInfo.mLastNameProperty, result.Lastname);
                                LoadProperty(userInfo, Common.UserInfo.mEMailProperty, result.EMail);

                                // Make sure that CSLA knows this has changed since being instantiated
                                MarkOld(userInfo);

                                return userInfo;
                            }
                        }
                        catch (DataAccessException dae)
                        {
                            mLog.Error(dae.ToString());
                            throw dae;
                        }
                        catch (Exception ex)
                        {
                            mLog.Error(ex.ToString());
                            throw ex;
                        }
                    }

                    /// <summary>
                    /// Method used to fetch an existing (Read Only) User from the database given a username
                    /// </summary>
                    /// <param name="criteria">The criteria object holding the username of the User to retrieve</param>
                    /// <returns>The (read only) User object fetched from the database</returns>
                    public Common.UserInfo FetchInfo(SingleCriteria<string> criteria)
                    {
                        try
                        {
                            using (var manager = ContextManager<MUM_dev>.GetManager(Database.BusinessSystem))
                            {
                                // Create an instance to return
                                var userInfo = (Common.UserInfo)MethodCaller.CreateInstance(typeof(Common.UserInfo));

                                // Fetch the data from the database based on the criteria
                                var selectResult = manager.DataContext.UspUserSelectByUsername(criteria.Value);

                                // Get the result
                                UspUserSelectByUsernameResult result = null;
                                try
                                {
                                    result = selectResult.Single();
                                }
                                catch (InvalidOperationException ioe)
                                {
                                    // Construct error message
                                    StringBuilder errorMessage = new StringBuilder();
                                    errorMessage.Append("Unable to locate exactly one User with Username = [ ");
                                    errorMessage.Append(criteria.Value);
                                    errorMessage.Append(" ]");
                                    mLog.Error(errorMessage.ToString(), ioe);
                                    throw new DataAccessException(errorMessage.ToString());
                                }

                                // Common properties inherited from MarketPulseBase<Visit>
                                LoadProperty(userInfo, Common.UserInfo.GUIDProperty, result.UserGUID);
                                LoadProperty(userInfo, Common.UserInfo.ObsoleteProperty, result.Obsolete.Value);
                                LoadProperty(userInfo, Common.UserInfo.InsertDateTimeProperty, result.InsertDateTime.Value);
                                LoadProperty(userInfo, Common.UserInfo.InsertUserGUIDProperty, result.InsertUserGUID.Value);
                                LoadProperty(userInfo, Common.UserInfo.UpdateDateTimeProperty, result.UpdateDateTime.Value);
                                LoadProperty(userInfo, Common.UserInfo.UpdateUserGUIDProperty, result.UpdateUserGUID.Value);
                                // Other properties
                                LoadProperty(userInfo, Common.UserInfo.mUsernameProperty, criteria.Value);
                                LoadProperty(userInfo, Common.UserInfo.mFirstnameProperty, result.Firstname);
                                LoadProperty(userInfo, Common.UserInfo.mLastNameProperty, result.Lastname);
                                LoadProperty(userInfo, Common.UserInfo.mEMailProperty, result.EMail);

                                // Make sure that CSLA knows this has changed since being instantiated
                                MarkOld(userInfo);

                                return userInfo;
                            }
                        }
                        catch (DataAccessException dae)
                        {
                            mLog.Error(dae.ToString());
                            throw dae;
                        }
                        catch (Exception ex)
                        {
                            mLog.Error(ex.ToString());
                            throw ex;
                        }
                    }

                #endregion

                #region UserInfoList

                    /// <summary>
                    /// Method used to fetch a read only list of read only User objects from the database based on user role
                    /// </summary>
                    /// <param name="criteria">The criteria object holding the values used to retrieve the list</param>
                    /// <returns>A read only list of read only User objects retrieved from the database</returns>
                    public Common.UserInfoList FetchInfoList(CountryGuidAndUserRoleGuidCriteria criteria)
                    {
                        try
                        {
                            using (var manager = ContextManager<MUM_dev>.GetManager(Database.BusinessSystem))
                            {
                                // Create an instance to return
                                var userInfoList = (Common.UserInfoList)MethodCaller.CreateInstance(typeof(Common.UserInfoList));

                                // Fetch the data from the database based on the criteria
                                var selectResult = manager.DataContext.UspUserSelectByCountryGUIDAndUserRoleGUID(criteria.CountryGuid, criteria.UserRoleGuid);

                                SetIsReadOnly(userInfoList, false);

                                foreach (UspUserSelectByCountryGUIDAndUserRoleGUIDResult result in selectResult)
                                {
                                    Common.UserInfo userInfo = (Common.UserInfo)MethodCaller.CreateInstance(typeof(Common.UserInfo));

                                    // Common properties inherited from MarketPulseBase<UserInfo>
                                    LoadProperty(userInfo, Common.UserInfo.GUIDProperty, result.UserGUID.Value);
                                    LoadProperty(userInfo, Common.UserInfo.ObsoleteProperty, result.Obsolete.Value);
                                    LoadProperty(userInfo, Common.UserInfo.InsertUserGUIDProperty, result.InsertUserGUID.Value);
                                    LoadProperty(userInfo, Common.UserInfo.InsertDateTimeProperty, (SmartDate)result.InsertDateTime);
                                    LoadProperty(userInfo, Common.UserInfo.UpdateUserGUIDProperty, result.UpdateUserGUID.Value);
                                    LoadProperty(userInfo, Common.UserInfo.UpdateDateTimeProperty, (SmartDate)result.UpdateDateTime);
                                    // Other properties
                                    LoadProperty(userInfo, Common.UserInfo.mUsernameProperty, result.Username);
                                    LoadProperty(userInfo, Common.UserInfo.mPasswordProperty, result.Password);
                                    LoadProperty(userInfo, Common.UserInfo.mFirstnameProperty, result.Firstname);
                                    LoadProperty(userInfo, Common.UserInfo.mLastNameProperty, result.Lastname);
                                    LoadProperty(userInfo, Common.UserInfo.mFullNameProperty, userInfo.ToString());
                                    LoadProperty(userInfo, Common.UserInfo.mEMailProperty, result.EMail);
                                    LoadProperty(userInfo, Common.UserInfo.mFailedLogonAttemptsProperty, result.FailedLoginAttempts.Value);

                                    // Make sure that CSLA knows this has changed since being instantiated
                                    MarkOld(userInfo);

                                    // Add the item to the list
                                    userInfoList.Add(userInfo);
                                }
                                SetIsReadOnly(userInfoList, true);

                                // Make sure that CSLA knows this has changed since being instantiated
                                MarkOld(userInfoList);

                                return userInfoList;
                            }
                        }
                        catch (InvalidOperationException ioe)
                        {
                            mLog.Error(ioe.ToString());
                            throw ioe;
                        }
                        catch (Exception ex)
                        {
                            mLog.Error(ex.ToString());
                            throw ex;
                        }
                    }

                #endregion

            #endregion

        #endregion

    }
}